#pragma once

#include <array>
#include <span>
#include <cstring>
#include <string>
#include <stdexcept>
#include <arpa/inet.h>

namespace wb
{
namespace net
{
enum class domain_t : int
{
    IPv4 = AF_INET,
    IPv6 = AF_INET6
};

std::string_view to_string(domain_t d);

class ip_address
{
  public:
    inline static constexpr size_t ipv4_len{4};
    inline static constexpr size_t ipv6_len{16};
    inline static constexpr size_t ipv4_strlen{INET_ADDRSTRLEN};
    inline static constexpr size_t ipv6_strlen{INET6_ADDRSTRLEN};
    ip_address() = default;
    ip_address(std::span<const uint8_t> bin_addr, domain_t d = domain_t::IPv4);
    ip_address(const ip_address&) = default;
    ip_address(ip_address&&) = default;
    ip_address& operator=(const ip_address&) = default;
    ip_address& operator=(ip_address&&) = default;
    ~ip_address();
    auto operator<=>(const ip_address& rhs) const = default;

  public:
    auto domain() const
    {
        return domain_;
    }

    auto data() const
    {
        return std::span<const uint8_t>{data_.data(),
                                        domain_ == domain_t::IPv4 ? ipv4_len
                                                                  : ipv6_len};
    }

    static ip_address from_string(const std::string& address,
                                  domain_t d = domain_t::IPv4);

    std::string to_string() const;

  private:
    domain_t domain_{domain_t::IPv4};
    std::array<uint8_t, ipv6_len> data_{};
};
}  // namespace net
}  // namespace wb